package model

import (
	"database/sql/driver"
	"encoding/json"
	"errors"
	"fmt"
	"gorm.io/gorm"
)

type DietMeta struct {
	gorm.Model
	UserId uint `json:"user_id"`
}

type Diet struct {
	DietMeta
	DietField
}

const (
	Diet_Table    = "diets"
	Diet_Foods    = "foods"
	Diet_UserId   = "user_id"
	Diet_Time     = "created_at"
	Diet_Eat_Time = "eat_at"
	Diet_Type     = "type"
)

var Diet_Types = []string{
	"meaningless",
	"breakfast",
	"lunch",
	"dinner",
	"snack",
}

type DietField struct {
	PicId   Sid    `json:"picId"`
	Foods   Foods  `json:"foods"`
	Comment string `json:"comment"`
	Type    int    `json:"type"`
}

func (d *Diet) Create(db *gorm.DB) error {
	return db.Create(d).Error
}

func (d *Diet) Get(db *gorm.DB) error {
	return db.First(d).Error
}

func (d *Diet) Delete(db *gorm.DB) error {
	return db.Delete(d).Error
}

func (d *Diet) Save(db *gorm.DB) error {
	return db.Save(d).Error
}

func (d *Diet) Updates(db *gorm.DB) error {
	return db.Updates(d).Error
}

func (d *Diet) SetType(diet string) {
	for i, v := range Diet_Types {
		if v == diet {
			d.Type = i
			return
		}
	}
	d.Type = 0
}

type Food struct {
	Name      string `json:"name"`
	Nutrition []struct {
		Key   string `json:"key"`
		Value string `json:"value"`
	} `json:"nutrition"`
}

type Foods struct {
	MaskSid  int64  `json:"maskSid"`
	FoodList []Food `json:"foodList"`
}

func (j *Foods) Scan(value interface{}) error {
	bytes, ok := value.([]byte)
	if !ok {
		return errors.New(fmt.Sprint("Failed to unmarshal JSONB value:", value))
	}
	err := json.Unmarshal(bytes, &j)
	return err
}

func (j Foods) Value() (driver.Value, error) {
	return json.Marshal(j)
}

type DietView struct {
	ID      uint   `json:"id"`
	PicId   string `json:"picId"`
	Foods   []Food `json:"foods"`
	Time    int64  `json:"time"`
	Comment string `json:"comment"`
	Type    string `json:"type"`
}

func FormatDietView(field *Diet) DietView {
	return DietView{
		ID:      field.ID,
		PicId:   field.PicId.String(),
		Foods:   field.Foods.FoodList,
		Time:    field.CreatedAt.Unix(),
		Comment: field.Comment,
		Type:    Diet_Types[field.Type],
	}
}

type DietSelectConditions struct {
	Start Time `json:"start"`
	End   Time `json:"end"`
}

func ListDiets(db *gorm.DB, where map[string]any, conditions *DietSelectConditions) []Diet {
	var diets []Diet
	sql := db.Table(Diet_Table).Where(where)

	if conditions.Start.Time.Unix() > 0 {
		sql = sql.Where(GtEq(Diet_Time, conditions.Start.Time))
	}
	if conditions.End.Time.Unix() > 0 {
		sql = sql.Where(LtEq(Diet_Time, conditions.End.Time))
	}
	sql.Find(&diets)
	return diets
}

func ListDietViews(db *gorm.DB, where map[string]any, conditions *DietSelectConditions) []DietView {
	diets := []DietView{}
	rows := ListDiets(db, where, conditions)
	for i := range rows {
		diets = append(diets, FormatDietView(&rows[i]))
	}
	return diets
}
